#!/usr/bin/env python
"""

    compress_bedgraph.py
    [--log_file PATH]
    [--verbose]

"""

################################################################################
#
#   compress_bedgraph
#
#
#   Copyright (c) 4/8/2011 Leo Goodstadt
#
#   Permission is hereby granted, free of charge, to any person obtaining a copy
#   of this software and associated documentation files (the "Software"), to deal
#   in the Software without restriction, including without limitation the rights
#   to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
#   copies of the Software, and to permit persons to whom the Software is
#   furnished to do so, subject to the following conditions:
#
#   The above copyright notice and this permission notice shall be included in
#   all copies or substantial portions of the Software.
#
#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
#   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
#   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
#   AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
#   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
#   OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
#   THE SOFTWARE.
#################################################################################

import sys, os

# add self to search path for testing
if __name__ == '__main__':
    exe_path = os.path.split(os.path.abspath(sys.argv[0]))[0]
    module_name = os.path.split(sys.argv[0])[1]
    module_name = os.path.splitext(module_name)[0];
else:
    module_name = __name__

# Use import path from <<../python_modules>>
if __name__ == '__main__':
    sys.path.append(os.path.abspath(os.path.join(exe_path,"..", "python_modules")))



#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   options


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888


if __name__ == '__main__':
    from optparse import OptionParser
    import StringIO

    parser = OptionParser(version="%prog 1.0", usage = "\n\n    %progs [options]")
    parser.add_option("-i", "--input_file", dest="input_file",
                      metavar="FILE",
                      type="string",
                      help="Name and path of input file.")
    parser.add_option("-o", "--output_file", dest="output_file",
                      metavar="FILE",
                      type="string",
                      help="Name and path of output file.")
    parser.add_option("-c", "--cutoff", dest="cutoff",
                      metavar="INTEGER",
                      type="int",
                      default = 10,
                      help="Merge adjacent intervals if they are less than the specified cutoff.")

    #
    #   general options: verbosity / logging
    #
    parser.add_option("-v", "--verbose", dest = "verbose",
                      action="count", default=0,
                      help="Print more verbose messages for each additional verbose level.")
    parser.add_option("-L", "--log_file", dest="log_file",
                      metavar="FILE",
                      type="string",
                      help="Name and path of log file")
    parser.add_option("--skip_parameter_logging", dest="skip_parameter_logging",
                        action="store_true", default=False,
                        help="Do not print program parameters to log.")
    parser.add_option("--debug", dest="debug",
                        action="count", default=0,
                        help="Set default program parameters in debugging mode.")




    # get help string
    f =StringIO.StringIO()
    parser.print_help(f)
    helpstr = f.getvalue()
    original_args = " ".join(sys.argv)
    (options, remaining_args) = parser.parse_args()


    #vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
    #                                             #
    #   Debug: Change these                       #
    #                                             #
    #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    if not options.input_file:
        options.input_file = "/data/mus/lg/projects/dnase_hypersensitivity/display_on_ucsc/akr.chr11_region.bedgraph"
    if not options.output_file:
        options.output_file = "/data/mus/lg/projects/dnase_hypersensitivity/display_on_ucsc/akr.chr11_region.compressed.bedgraph"
    #if not options.input_file:
    #    options.input_file = "/data/mus/lg/projects/dnase_hypersensitivity/display_on_ucsc/aj.chr11_region.bedgraph"
    #if not options.output_file:
    #    options.output_file = "/data/mus/lg/projects/dnase_hypersensitivity/display_on_ucsc/aj.chr11_region.compressed.bedgraph"

    if not options.log_file:
        options.log_file                = os.path.join("compress_bedgraph.log")
    options.verbose                 = 5
    #vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
    #                                             #
    #   Debug: Change these                       #
    #                                             #
    #^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

    #
    #   mandatory options
    #
    mandatory_options = []
    def check_mandatory_options (options, mandatory_options, helpstr):
        """
        Check if specified mandatory options have b een defined
        """
        missing_options = []
        for o in mandatory_options:
            if not getattr(options, o):
                missing_options.append("--" + o)

        if not len(missing_options):
            return

        raise Exception("Missing mandatory parameter%s: %s.\n\n%s\n\n" %
                        ("s" if len(missing_options) > 1 else "",
                         ", ".join(missing_options),
                         helpstr))
    check_mandatory_options (options, mandatory_options, helpstr)


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   imports


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888


#from json import dumps
#from collections import defaultdict



#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   Functions


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   Logger


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

if __name__ == '__main__':
    import logging
    import logging.handlers

    MESSAGE = 15
    logging.addLevelName(MESSAGE, "MESSAGE")

    def setup_std_logging (logger, log_file, verbose):
        """
        set up logging using programme options
        """
        class debug_filter(logging.Filter):
            """
            Ignore INFO messages
            """
            def filter(self, record):
                return logging.INFO != record.levelno

        class NullHandler(logging.Handler):
            """
            for when there is no logging
            """
            def emit(self, record):
                pass

        # We are interesting in all messages
        logger.setLevel(logging.DEBUG)
        has_handler = False

        # log to file if that is specified
        if log_file:
            handler = logging.FileHandler(log_file, delay=False)
            class stipped_down_formatter(logging.Formatter):
                def format(self, record):
                    prefix = ""
                    if not hasattr(self, "first_used"):
                        self.first_used = True
                        prefix = "\n" + self.formatTime(record, "%Y-%m-%d")
                        prefix += " %(name)s\n" % record.__dict__
                    if record.levelname in ("INFO", "MESSAGE", "DEBUG"):
                        self._fmt = " %(asctime)s - %(message)s"
                    else:
                        self._fmt = " %(asctime)s - %(levelname)-7s - %(message)s"
                    return prefix + logging.Formatter.format(self, record)
            handler.setFormatter(stipped_down_formatter("%(asctime)s - %(name)s - %(levelname)6s - %(message)s", "%H:%M:%S"))
            handler.setLevel(MESSAGE)
            logger.addHandler(handler)
            has_handler = True

        # log to stderr if verbose
        if verbose:
            stderrhandler = logging.StreamHandler(sys.stderr)
            stderrhandler.setFormatter(logging.Formatter("    %(message)s"))
            stderrhandler.setLevel(logging.DEBUG)
            if log_file:
                stderrhandler.addFilter(debug_filter())
            logger.addHandler(stderrhandler)
            has_handler = True

        # no logging
        if not has_handler:
            logger.addHandler(NullHandler())


    #
    #   set up log
    #
    logger = logging.getLogger(module_name)
    setup_std_logging(logger, options.log_file, options.verbose)


    #
    #   log programme parameters
    #
    if not options.skip_parameter_logging:
        programme_name = os.path.split(sys.argv[0])[1]
        logger.info(programme_name)

#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888

#   Main logic


#88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888
if __name__ == '__main__':

    #
    #   open input and output files
    #
    input_file = open(options.input_file)
    output_file = open(options.output_file, "w")

    #
    #   header passes through unchanged
    #
    firstline = input_file.next()
    output_file.write(firstline)

    sys.stderr.write("\t[")

    #
    #   current interval
    #
    curr_chrm = None
    curr_beg  = None
    curr_end  = None
    curr_val  = None

    for cnt_lines, line in enumerate(input_file):

        # write a dot every Nth rows to show progress
        if cnt_lines % 100000 == 0:
            sys.stderr.write(".")

        # parse fields
        chrm, beg, end, value = line.strip("\n").split("\t")

        # convert to numbers
        beg, end, value = map(int, (beg, end, value))

        # lines > cutoff are written directly
        if value > options.cutoff:
            if curr_chrm != None:
                # print out merged interval (using the average as the value)
                output_file.write("%s\t%d\t%d\t%d\n" % (curr_chrm, curr_beg, curr_end, curr_val / (curr_end - curr_beg)))
                curr_chrm = None
            output_file.write(line)

        #
        #   write interval: change in chromosome
        #
        elif curr_chrm != chrm or (curr_end != beg and beg - curr_end > 1000):
            # print out merged interval (using the average as the value)
            if curr_chrm:
                output_file.write("%s\t%d\t%d\t%d\n" % (curr_chrm, curr_beg, curr_end, curr_val / (curr_end - curr_beg)))
            curr_chrm = chrm
            curr_beg = beg
            curr_end = end
            curr_val = (end - beg) * value


        #
        #   contigous interval under threshold, add up
        #
        else:
            curr_end = end
            curr_val += (end - beg) * value


    #
    #   write last inteval
    #
    if curr_chrm != None:
        # print out merged interval (using the average as the value)
        output_file.write("%s\t%d\t%d\t%d\n" % (curr_chrm, curr_beg, curr_end, curr_val / (curr_end - curr_beg)))

    sys.stderr.write("]\n")

    logger.log(MESSAGE, "%5d lines in input file" % cnt_lines)


